package net.npe.texturedcube.client;
import com.google.gwt.canvas.client.Canvas;
import com.google.gwt.canvas.dom.client.CanvasPixelArray;
import com.google.gwt.canvas.dom.client.Context2d;
import com.google.gwt.canvas.dom.client.ImageData;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.typedarrays.client.Uint8ArrayNative;
import com.google.gwt.typedarrays.shared.ArrayBuffer;
import com.google.gwt.user.client.ui.RootLayoutPanel;
import com.google.gwt.xhr.client.ReadyStateChangeHandler;
import com.google.gwt.xhr.client.XMLHttpRequest;
import com.google.gwt.xhr.client.XMLHttpRequest.ResponseType;
import com.googlecode.gwtgl.array.Float32Array;
import com.googlecode.gwtgl.binding.WebGLBuffer;
import com.googlecode.gwtgl.binding.WebGLProgram;
import com.googlecode.gwtgl.binding.WebGLRenderingContext;
import com.googlecode.gwtgl.binding.WebGLShader;
import com.googlecode.gwtgl.binding.WebGLTexture;
import com.googlecode.gwtgl.binding.WebGLUniformLocation;
import static com.googlecode.gwtgl.binding.WebGLRenderingContext.*;
/**
* Entry point classes define <code>onModuleLoad()</code>.
*/
public class TexturedCube implements EntryPoint {
private float [] VERTICES = {
// position.xyz, texcoord.xy
// front
-0.5f, +0.5f, +0.5f, 0.f, 0.f,
-0.5f, -0.5f, +0.5f, 0.f, 1.f,
+0.5f, +0.5f, +0.5f, 1.f, 0.f,
+0.5f, -0.5f, +0.5f, 1.f, 1.f,
// back
+0.5f, +0.5f, -0.5f, 0.f, 0.f,
+0.5f, -0.5f, -0.5f, 0.f, 1.f,
-0.5f, +0.5f, -0.5f, 1.f, 0.f,
-0.5f, -0.5f, -0.5f, 1.f, 1.f,
};
private String VERTEX_SHADER_SOURCE =
"attribute vec3 position;"+
"attribute vec2 texcoord;"+
"varying vec2 vTexcoord;"+
"uniform mat4 MVP;"+
"void main() { gl_Position = MVP * vec4(position, 1.0); vTexcoord = texcoord; }";
private String FRAGMENT_SHADER_SOURCE =
"precision highp float;"+
"varying vec2 vTexcoord;"+
"uniform sampler2D texture;"+
"void main() { gl_FragColor = texture2D(texture, vTexcoord); }";
private float [][] MODELMATRICES = {
{ // none
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
},
{ // rotY90
0, 0, -1, 0,
0, 1, 0, 0,
1, 0, 0, 0,
0, 0, 0, 1,
},
{ // rotX-90
1, 0, 0, 0,
0, 0, -1, 0,
0, 1, 0, 0,
0, 0, 0, 1,
},
};
public static final String [] TEXTURE_URLS = {
"images/rgb_rle_LL.tga",
"images/grayscale_rle_LL.tga",
"images/indexed_rle_LL.tga",
};
public void onModuleLoad() {
Canvas canvas = Canvas.createIfSupported();
canvas.setStyleName("MyCanvas");
canvas.setCoordinateSpaceWidth(400);
canvas.setCoordinateSpaceHeight(400);
RootLayoutPanel.get().add(canvas);
gl = (WebGLRenderingContext)canvas.getContext("experimental-webgl");
gl.viewport(0, 0, 400, 400);
WebGLBuffer vertexBuffer = gl.createBuffer();
gl.bindBuffer(ARRAY_BUFFER, vertexBuffer);
gl.bufferData(ARRAY_BUFFER, Float32Array.create(VERTICES), STATIC_DRAW);
WebGLShader vertexShader = gl.createShader(VERTEX_SHADER);
gl.shaderSource(vertexShader, VERTEX_SHADER_SOURCE);
gl.compileShader(vertexShader);
WebGLShader fragmentShader = gl.createShader(FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, FRAGMENT_SHADER_SOURCE);
gl.compileShader(fragmentShader);
program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
gl.bindBuffer(ARRAY_BUFFER, vertexBuffer);
WebGLUniformLocation texture = gl.getUniformLocation(program, "texture");
gl.uniform1i(texture, 0);
int posAttr = gl.getAttribLocation(program, "position");
gl.vertexAttribPointer(posAttr, 3, FLOAT, false, 5*4, 0);
gl.enableVertexAttribArray(posAttr);
int texAttr = gl.getAttribLocation(program, "texcoord");
gl.vertexAttribPointer(texAttr, 2, FLOAT, false, 5*4, 3*4);
gl.enableVertexAttribArray(texAttr);
for(int i=0; i<TEXTURE_URLS.length; i++) {
loadTexture(TEXTURE_URLS[i], i);
}
}
void draw() {
gl.clearColor(0.5f, 0.5f, 0.5f, 1);
gl.clear(COLOR_BUFFER_BIT);
gl.enable(CULL_FACE);
gl.cullFace(BACK);
gl.enable(BLEND);
gl.blendFunc(SRC_ALPHA, ONE_MINUS_SRC_ALPHA);
float [] view = new float[16];
lookAt(view, 2, 2, 2, 0, 0, 0, 0, 1, 0);
float [] proj = new float[16];
perspective(proj, 45, 1.f, 1.f, 10.f);
float [] vp = new float[16];
multiply(vp, view, proj);
float [] mvp = new float[16];
for(int i=0; i<3; i++) {
multiply(mvp, MODELMATRICES[i], vp);
draw(mvp, textures[i]);
}
}
void draw(float [] mvp, WebGLTexture texture) {
WebGLUniformLocation MVP = gl.getUniformLocation(program, "MVP");
gl.uniformMatrix4fv(MVP, false, mvp);
gl.bindTexture(TEXTURE_2D, texture);
gl.activeTexture(TEXTURE0);
gl.drawArrays(TRIANGLE_STRIP, 0, 4);
gl.drawArrays(TRIANGLE_STRIP, 4, 4);
}
Canvas createImageCanvas(int [] pixels, int width, int height) {
Canvas canvas = Canvas.createIfSupported();
canvas.setCoordinateSpaceWidth(width);
canvas.setCoordinateSpaceHeight(height);
Context2d context = canvas.getContext2d();
ImageData data = context.createImageData(width, height);
CanvasPixelArray array = data.getData();
for(int i=0; i<width*height; i++) {
array.set(4*i+0, pixels[i] & 0xFF);
array.set(4*i+1, (pixels[i] >> 8) & 0xFF);
array.set(4*i+2, (pixels[i] >> 16) & 0xFF);
array.set(4*i+3, (pixels[i] >> 24) & 0xFF);
}
context.putImageData(data, 0, 0);
return canvas;
}
void loadTexture(String url, int index) {
final int i = index;
XMLHttpRequest request = XMLHttpRequest.create();
request.open("GET", url);
request.setResponseType(ResponseType.ArrayBuffer);
request.setOnReadyStateChange(new ReadyStateChangeHandler() {
@Override
public void onReadyStateChange(XMLHttpRequest xhr) {
if(xhr.getReadyState() == XMLHttpRequest.DONE) {
if(xhr.getStatus() >= 400) {
// error
System.out.println("Error");
}
else {
try {
ArrayBuffer arrayBuffer = xhr.getResponseArrayBuffer();
Uint8ArrayNative u8array = Uint8ArrayNative.create(arrayBuffer);
byte [] buffer = new byte[u8array.length()];
for(int i=0; i<buffer.length; i++) {
buffer[i] = (byte)u8array.get(i);
}
int [] pixels = TGAReader.read(buffer, TGAReader.ABGR);
int width = TGAReader.getWidth(buffer);
int height = TGAReader.getHeight(buffer);
Canvas canvas = createImageCanvas(pixels, width, height);
WebGLTexture texture = gl.createTexture();
gl.enable(TEXTURE_2D);
gl.bindTexture(TEXTURE_2D, texture);
gl.texImage2D(TEXTURE_2D, 0, RGBA, RGBA, UNSIGNED_BYTE, canvas.getElement());
gl.texParameteri(TEXTURE_2D, TEXTURE_WRAP_S, CLAMP_TO_EDGE);
gl.texParameteri(TEXTURE_2D, TEXTURE_WRAP_T, CLAMP_TO_EDGE);
gl.texParameteri(TEXTURE_2D, TEXTURE_MAG_FILTER, LINEAR);
gl.texParameteri(TEXTURE_2D, TEXTURE_MIN_FILTER, LINEAR);
textures[i] = texture;
draw();
}
catch(Exception e) {
e.printStackTrace();
}
}
}
}
});
request.send();
}
public static void multiply(float [] m0, float [] m1, float [] m2) {
m0[ 0] = m1[ 0]*m2[ 0] + m1[ 1]*m2[ 4] + m1[ 2]*m2[ 8] + m1[ 3]*m2[12];
m0[ 1] = m1[ 0]*m2[ 1] + m1[ 1]*m2[ 5] + m1[ 2]*m2[ 9] + m1[ 3]*m2[13];
m0[ 2] = m1[ 0]*m2[ 2] + m1[ 1]*m2[ 6] + m1[ 2]*m2[10] + m1[ 3]*m2[14];
m0[ 3] = m1[ 0]*m2[ 3] + m1[ 1]*m2[ 7] + m1[ 2]*m2[11] + m1[ 3]*m2[15];
m0[ 4] = m1[ 4]*m2[ 0] + m1[ 5]*m2[ 4] + m1[ 6]*m2[ 8] + m1[ 7]*m2[12];
m0[ 5] = m1[ 4]*m2[ 1] + m1[ 5]*m2[ 5] + m1[ 6]*m2[ 9] + m1[ 7]*m2[13];
m0[ 6] = m1[ 4]*m2[ 2] + m1[ 5]*m2[ 6] + m1[ 6]*m2[10] + m1[ 7]*m2[14];
m0[ 7] = m1[ 4]*m2[ 3] + m1[ 5]*m2[ 7] + m1[ 6]*m2[11] + m1[ 7]*m2[15];
m0[ 8] = m1[ 8]*m2[ 0] + m1[ 9]*m2[ 4] + m1[10]*m2[ 8] + m1[11]*m2[12];
m0[ 9] = m1[ 8]*m2[ 1] + m1[ 9]*m2[ 5] + m1[10]*m2[ 9] + m1[11]*m2[13];
m0[10] = m1[ 8]*m2[ 2] + m1[ 9]*m2[ 6] + m1[10]*m2[10] + m1[11]*m2[14];
m0[11] = m1[ 8]*m2[ 3] + m1[ 9]*m2[ 7] + m1[10]*m2[11] + m1[11]*m2[15];
m0[12] = m1[12]*m2[ 0] + m1[13]*m2[ 4] + m1[14]*m2[ 8] + m1[15]*m2[12];
m0[13] = m1[12]*m2[ 1] + m1[13]*m2[ 5] + m1[14]*m2[ 9] + m1[15]*m2[13];
m0[14] = m1[12]*m2[ 2] + m1[13]*m2[ 6] + m1[14]*m2[10] + m1[15]*m2[14];
m0[15] = m1[12]*m2[ 3] + m1[13]*m2[ 7] + m1[14]*m2[11] + m1[15]*m2[15];
}
public static void lookAt(float [] m, float ex, float ey, float ez, float cx, float cy, float cz, float ux, float uy, float uz) {
float fx = ex - cx;
float fy = ey - cy;
float fz = ez - cz;
float rlf = 1.f / (float)Math.sqrt(fx*fx + fy*fy + fz*fz);
m[2] = rlf * fx;
m[6] = rlf * fy;
m[10] = rlf * fz;
float sx = m[10] * uy - m[6] * uz;
float sy = m[2] * uz - m[10] * ux;
float sz = m[6] * ux - m[2] * uy;
float rls = 1.f / (float)Math.sqrt(sx*sx + sy*sy + sz*sz);
m[0] = rls * sx;
m[4] = rls * sy;
m[8] = rls * sz;
m[1] = m[6] * m[8] - m[10] * m[4];
m[5] = m[10] * m[0] - m[2] * m[8];
m[9] = m[2] * m[4] - m[6] * m[0];
m[12] = -(ex * m[0] + ey * m[4] + ez * m[8]);
m[13] = -(ex * m[1] + ey * m[5] + ez * m[9]);
m[14] = -(ex * m[2] + ey * m[6] + ez * m[10]);
m[3] = m[7] = m[11] = 0;
m[15] = 1;
}
public static void perspective(float [] m, float fov, float aspect, float near, float far) {
float top = near * (float)Math.tan(fov * Math.PI / 360.0);
float bottom = -top;
float left = bottom * aspect;
float right = top * aspect;
m[0] = 2 * near / (right - left);
m[1] = 0;
m[2] = 0;
m[3] = 0;
m[4] = 0;
m[5] = 2 * near / (top - bottom);
m[6] = 0;
m[7] = 0;
m[8] = (right + left) / (right - left);
m[9] = (top + bottom) / (top - bottom);
m[10] = -(far + near) / (far - near);
m[11] = -1;
m[12] = 0;
m[13] = 0;
m[14] = -2 * far * near / (far - near);
m[15] = 0;
}
private WebGLRenderingContext gl;
private WebGLProgram program;
private WebGLTexture [] textures = new WebGLTexture[3];
}